/*
 * Copyright 2001-2009 Terracotta, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package org.go.scheduler.database.concurrent;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.go.expcetion.LockException;
import org.go.util.Util;

/**
 * Provide thread/resource locking in order to protect
 * resources from being altered by multiple threads at the same time using
 * a db row update.
 * 
 * <p>
 * <b>Note:</b> This Semaphore implementation is useful for databases that do
 * not support row locking via "SELECT FOR UPDATE" type syntax, for example
 * Microsoft SQLServer (MSSQL).
 * </p> 
 */
public class UpdateLockRowSemaphore extends DBSemaphore {

	public static final String INSERT_LOCK = "INSERT INTO " + TABLE_PREFIX_SUBST + TABLE_LOCKS + "(" + COL_SCHEDULER_NAME + ", " + COL_LOCK_NAME + ") VALUES (" + SCHED_NAME_SUBST + ", ?)";

	public static final String UPDATE_FOR_LOCK = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_LOCKS + " SET " + COL_LOCK_NAME + " = " + COL_LOCK_NAME + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + " AND " + COL_LOCK_NAME + " = ? ";

	public UpdateLockRowSemaphore() {
		super(DEFAULT_TABLE_PREFIX, null, UPDATE_FOR_LOCK, INSERT_LOCK);
	}

	/**
	 * Execute the SQL select for update that will lock the proper database row.
	 */
	@Override
	protected void executeSQL(Connection conn, String lockName, String expandedSQL, String expandedInsertSQL) throws LockException {
		PreparedStatement ps = null;

		try {
			ps = conn.prepareStatement(expandedSQL);
			ps.setString(1, lockName);

			if (getLog().isDebugEnabled()) {
				getLog().debug("Lock '" + lockName + "' is being obtained: " + Thread.currentThread().getName());
			}

			int numUpdate = ps.executeUpdate();

			if (numUpdate < 1) {
				getLog().debug("Inserting new lock row for lock: '" + lockName + "' being obtained by thread: " + Thread.currentThread().getName());
				ps = conn.prepareStatement(expandedInsertSQL);
				ps.setString(1, lockName);

				int res = ps.executeUpdate();

				if (res != 1)
					throw new SQLException(Util.rtp("No row exists, and one could not be inserted in table " + TABLE_PREFIX_SUBST + TABLE_LOCKS + " for lock named: " + lockName, getTablePrefix(), getSchedulerNameLiteral()));

			}
		} catch (SQLException sqle) {
			//Exception src =
			// (Exception)getThreadLocksObtainer().get(lockName);
			//if(src != null)
			//  src.printStackTrace();
			//else
			//  System.err.println("--- ***************** NO OBTAINER!");

			if (getLog().isDebugEnabled()) {
				getLog().debug("Lock '" + lockName + "' was not obtained by: " + Thread.currentThread().getName());
			}

			throw new LockException("Failure obtaining db row lock: " + sqle.getMessage(), sqle);
		} finally {
			if (ps != null) {
				try {
					ps.close();
				} catch (Exception ignore) {
				}
			}
		}
	}

	protected String getUpdateLockRowSQL() {
		return getSQL();
	}

	public void setUpdateLockRowSQL(String updateLockRowSQL) {
		setSQL(updateLockRowSQL);
	}
}
